
module mdio_ctrl(
    input           i_clk               ,
    input           i_rst               ,

    output          o_link              ,
    output [1 :0]   o_speed             ,

    output [1 :0]   o_user_op           ,
    output [4 :0]   o_user_phy_addr     ,
    output [4 :0]   o_user_reg_addr     ,
    output [15:0]   o_user_reg_data     ,
    output          o_user_valid        ,
    input           i_user_ready        ,
    input  [15:0]   i_user_read_data    ,
    input           i_user_read_valid   
);


localparam          P_ST_ILDE      = 0  ,
                    P_ST_READ_LINK = 1  ,
                    P_ST_WAIT_LINK = 2  ,
                    P_ST_READ_SPEED= 3  ,
                    P_ST_WAIT_SPEED= 4  ,
                    P_ST_END       = 5  ;

reg  [7 :0]         r_st_current        ;
reg  [7 :0]         r_st_next           ;
reg  [15:0]         r_st_cnt            ;


reg                 ro_link             ;
reg  [1 :0]         ro_speed            ;
reg  [1 :0]         ro_user_op          ;
reg  [4 :0]         ro_user_phy_addr    ;
reg  [4 :0]         ro_user_reg_addr    ;
reg  [15:0]         ro_user_reg_data    ;
reg                 ro_user_valid       ;
reg  [15:0]         ri_user_read_data   ; 
reg                 ri_user_read_valid  ; 
reg  [1 :0]         ri_user_ready       ;

wire                w_active            ;

assign o_link          = ro_link            ;
assign o_speed         = ro_speed           ;
assign o_user_op       = ro_user_op         ;
assign o_user_phy_addr = ro_user_phy_addr   ;
assign o_user_reg_addr = ro_user_reg_addr   ;
assign o_user_reg_data = ro_user_reg_data   ;
assign o_user_valid    = ro_user_valid      ;
assign w_active        = o_user_valid & i_user_ready;

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_st_current <= 'd0;
    else 
        r_st_current <= r_st_next;
end

always@(*)
begin
    case (r_st_current)
        P_ST_ILDE       :r_st_next = r_st_cnt == 100    ? P_ST_READ_LINK    : P_ST_ILDE         ;    
        P_ST_READ_LINK  :r_st_next = w_active           ? P_ST_WAIT_LINK    : P_ST_READ_LINK    ;
        P_ST_WAIT_LINK  :r_st_next = r_st_cnt > 2 && ri_user_ready[1]   ? P_ST_READ_SPEED   : P_ST_WAIT_LINK    ;
        P_ST_READ_SPEED :r_st_next = w_active           ? P_ST_WAIT_SPEED   : P_ST_READ_SPEED   ;
        P_ST_WAIT_SPEED :r_st_next = r_st_cnt > 2 && ri_user_ready[1]   ? P_ST_END          : P_ST_WAIT_SPEED   ;
        P_ST_END        :r_st_next = P_ST_ILDE;
        default         :r_st_next = P_ST_ILDE;
    endcase
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_st_cnt <= 'd0;
    else if(r_st_current != r_st_next)
        r_st_cnt <= 'd0;
    else 
        r_st_cnt <= r_st_cnt + 1;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ri_user_ready <= 'd0;
    else 
        ri_user_ready <= {ri_user_ready[0],i_user_ready};
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst) begin
        ro_user_op       <= 'd0;
        ro_user_phy_addr <= 'd0;
        ro_user_reg_addr <= 'd0;
        ro_user_reg_data <= 'd0;
        ro_user_valid    <= 'd0;
    end else if(w_active) begin
        ro_user_op       <= 2'b10;
        ro_user_phy_addr <= 'd1;
        ro_user_reg_addr <= 'd1;
        ro_user_reg_data <= 'd0;
        ro_user_valid    <= 'd0;
    end else if(r_st_current == P_ST_READ_LINK) begin//read link
        ro_user_op       <= 2'b10;
        ro_user_phy_addr <= 'd1;
        ro_user_reg_addr <= 'd1;
        ro_user_reg_data <= 'd0;
        ro_user_valid    <= 'd1;
    end else if(r_st_current == P_ST_READ_SPEED) begin//read speed
        ro_user_op       <= 2'b10;
        ro_user_phy_addr <= 'd1;
        ro_user_reg_addr <= 'd0;
        ro_user_reg_data <= 'd0;
        ro_user_valid    <= 'd1;
    end else begin
        ro_user_op       <= ro_user_op      ;
        ro_user_phy_addr <= ro_user_phy_addr;
        ro_user_reg_addr <= ro_user_reg_addr;
        ro_user_reg_data <= ro_user_reg_data;
        ro_user_valid    <= ro_user_valid;
    end
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst) begin
        ri_user_read_data  <= 'd0;
        ri_user_read_valid <= 'd0;
    end else begin
        ri_user_read_data  <= i_user_read_data ;
        ri_user_read_valid <= i_user_read_valid;
    end
end 

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_link <= 'd0;
    else if(r_st_current == P_ST_WAIT_LINK && ri_user_read_valid)
        ro_link <= ri_user_read_data[2];
    else
        ro_link <= ro_link;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_speed <= 'd0;
    else if(r_st_current == P_ST_WAIT_SPEED && ri_user_read_valid)
        ro_speed <= {ri_user_read_data[6],ri_user_read_data[13]};
    else
        ro_speed <= ro_speed;
end

endmodule
